#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <pthread.h>
#include "include.h"

struct memory ma_mem;
struct sfile swap;

struct block thr_block;

size_t count = 0; 
size_t next;
int exist = 0;

//-----------------------------------------------------------------------------------------

int ma_init(size_t mem, off_t sw, const char* swap_path){	
	init_logger();
	if(exist) {
		errno = EALREADY;
		return 0;
		}

	if( mem > sw){
		errno = EBADR;		
		return 0;
		}
	
	swap.fd = open(swap_path, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR | S_IWUSR); 
	if(!(swap.fd)){
		if( errno != EACCES) errno = EFAULT;
		return 0;
		}

	lseek (swap.fd, sw + 1, SEEK_SET); 
	write (swap.fd, "", 1);
	lseek (swap.fd, 0, SEEK_SET);

	swap.n = sw;
	swap.free = sw;

	ma_mem.st = malloc(mem); 
	ma_mem.n = mem;

	if(!(ma_mem.st)) {
			errno = ENOMEM;
			return 0;
			}
	
	thr_block.st = (struct data**)malloc(SPART*sizeof(struct data*));
	(thr_block.st)[0] = (struct data*)malloc(sizeof(struct data));
	(thr_block.st)[0]->n = 0;
	(thr_block.st)[0]->sz = 0;
	(thr_block.st)[0]->rl = 0;

	struct data* one = (struct data*)malloc(sizeof(struct data));
	one->n = 0;
	one->sz = sw;
	one->rl = 0;
	one->last = thr_block.st[0];
	one->next = thr_block.st[0];
	(thr_block.st)[0]->next = one;
	(thr_block.st)[0]->last = one;

	thr_block.sz = (size_t *)malloc(SPART*sizeof(size_t));	
	(thr_block.sz)[0] = 2;

	thr_block.n = 1;

	next = 1;
	exist = 1;
	
	return 1;
};

//------------------------------------------------------------------------

void ma_deinit(){
	munmap (ma_mem.st, ma_mem.n);
	close(swap.fd);
	struct data *one, *two;
	size_t k;
	int i;
	for(k = 0; k < thr_block.n; k++){
		one = (thr_block.st)[k];
		for(i = 0 ; i < (thr_block.sz)[k]; i++) {
			two = one->next;
			free(one);
			one = two;			
				}
			}
	free(thr_block.st);
	free(thr_block.sz);
	exist = 0;
	};

//--------------------------------------------------------------------------

int ma_free(size_t id){ 
	if( !id){
		errno = EINVAL;
		return 0;
		}
	if(!exist) {
		errno = ECANCELED;
		return 0;
		}

	
	struct thr2* i;
	i = search(id, thr_block);
	
	if( i->two == 1 ){
		errno = EFAULT;		
		free(i);

		return 0;
		}
	
	if( i->two == 0 ){
		errno = EBUSY;
		free(i);
		return 0;
		}

	swap.free += (i->one)->sz;	
	int k = id_free(i->one, thr_block,i->four); 
	
	free(i);
	return 1;
};

//-------------------------------------------------------------------------------------------
	
size_t ma_alloc(size_t sz){
	if( !sz) {
		errno = EINVAL;
		return 0;
		}
	if(!exist) {
		errno = ECANCELED;
		return 0;
		}

	if( sz > swap.n){
		errno = E2BIG;
		return 0;
		}

	if( next == 0) {
		errno = EMLINK;
		return 0;
		}

	if( swap.free < sz){
		errno = ENOMEM;
		return 0;
		}

	size_t i = add(sz, thr_block, next);
	if(!i) {
		errno = ENOBUFS;
		return 0;
		}

	swap.free -= sz;
	thr_block.n = i;
	
	if( thr_block.n < SPART && count >= MCOUNT) {
		i = prepare1(thr_block);
		thr_block.n = i;
		count = 0;
			}	
	next++;
	count++;
	
	return next-1;      		 
	};

//-------------------------------------------------------------------

void* ma_get(size_t id){
	if( !id) {
		errno = EINVAL;
		return NULL;
		}
	if(!exist) {
		errno = ECANCELED;
		return NULL;
		}

	struct thr2* i;
	i = search(id, thr_block);
	
	if( i->two == 1 ){
		errno = EFAULT;
		free(i);
		return NULL;
		}
	
	if( (i->one)->sz > ma_mem.n ){
		errno = ENOMEM;
		free(i);
		return NULL;
		}	
	
	(i->one)->rl = 0;
	
	off_t pa_offset = (i->three) & ~(sysconf(_SC_PAGE_SIZE) - 1);
	off_t new_length = ((i->one)->sz) + (i->three) - pa_offset;
	off_t sd = (i->three) - pa_offset;

	void* one = mmap(ma_mem.st, new_length, PROT_READ|PROT_WRITE, MAP_SHARED, swap.fd, pa_offset); 
	if(one == MAP_FAILED) {
		errno = EBADF;
		free(i);
		return NULL;
		}
	
	free(i);
	return ma_mem.st+sd;
};

//--------------------------------------------------------------------------------

int ma_release(size_t id){
	if( !id) {
		errno = EINVAL;
		return 0;
		}
	if(!exist) {
		errno = ECANCELED;
		return 0;
		}

	struct thr2* i;
	i = search(id, thr_block);
	
	if( i->two == 1 ){
		errno = EFAULT;
		free(i);		
		return 0;
		}
	(i->one)->rl = 1; 
	
	free(i);
	return 1;	 
};
